package second00001;

import config.AppConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.ComplexBean4SetInjection;
import pojo.Person;
import pojo.User;
import second.UserService;

import java.util.Arrays;

/**
 * @author: ZhouBert
 * @date: 2020/7/26
 * @description: 测试 Spring Injection 注入
 * 1.什么是注入：
 * 通过 Spring 工厂 + 配置文件，为所创建对象的成员变量赋值。
 * <p>
 * 2.为什么需要注入：
 * 通过编码的方式，为成员变量进行赋值，存在耦合。
 * eg.通过 getBean -> person
 * person.setName("zhangsan")	这样就是硬编码，不能灵活改动 setName
 * <p>
 * 3.使用 Spring 进行 SET 注入的步骤：
 * -1- 类的成员变量提供 Set 方法；
 * -2- 配置 Spring 的配置文件
 * eg.XML 中的
 * <p>
 * 4.反思：注入的好处
 * 解耦和！
 * 避免了重新编译+重新部署的过程！-> 只需要改变配置文件即可
 *
 * 5.xml set 注入简化
 * - ref 标签简化
 * <properties name="userDAO" ref="userDAO"></properties>
 * - value 标签简化
 * <properties name="name" value="xxx"/>
 * - 命名空间 p(property ) 需要引用一个xml 协议标准（Ctrl+Enter 即可）
* <beans id="person" class="xx">
 * <property name="name" value=""></property>
 * <property name="id" value=""></property>
 * </beans>
 * --> 简化
 * <beans id="person" class="xx" p:name="" p:id="" p:userDAO-ref="userDAO" />
 *
 * 6.通过 有参构造方法实现注入
 * -1-类中提供有参构造方法
 * -2-xml 中配置 constructor-arg (每个都是一个参数)
 * <beans id="customer" class="xx">
 * <constructor-arg type="string">
 *     <value>suns</value>
 * </constructor-arg>
 * <constructor-arg type="int">
 *    <value>102</value>
 * </constructor-arg>
 * </beans>
 *
 * 7.总结 Spring 两种注入方式：
 * Spring 底层大量使用 Setter 注入！
 * Setter 注入的好处：官方文档说明可以解决循环依赖的问题！TODO:具体怎么解决不清楚- - 
 */
@Slf4j
public class TestApp4Injection {

	ApplicationContext ctx = new ClassPathXmlApplicationContext(AppConfig.XML_PATH);

	/**
	 * 测试成功由 配置文件实现注入！
	 */
	public void testInject() {
		Person bean = ctx.getBean("/p", Person.class);
		log.info(bean.toString());
	}

	/**
	 * 测试复杂类注入
	 */
	public void testInjectComplexBean() {
		ComplexBean4SetInjection bean = ctx.getBean(ComplexBean4SetInjection.class);
		log.info("id:{}", bean.getId());
		log.info("name:{}", bean.getName());
		log.info("array begin:");
		Arrays.stream(bean.getArrs()).forEach(p -> log.info(p));
		log.info("list begin:");
		bean.getLists().forEach(p -> log.info(p));
		log.info("sets begin:");
		bean.getSets().forEach(p -> log.info(p));
		log.info("maps begin:");
		bean.getMaps().forEach((k, v) -> log.info("{},{}", k, v));
		log.info("properties begin:");
		bean.getProperties().forEach((k, v) -> log.info("{},{}", k, v));
	}

	/**
	 * 自定义类型成员变量赋值
	 * 步骤：
	 * 1.为成员变量 UserDAO 赋予 Setter 方法；
	 * 2.xml 中进行编码
	 * <beans class="second.impl.UserServiceImplByInjection">
	 * 		<property name="userDAO">
	 * 			<beans class="second.UserDAOImpl"></beans>
	 * 		</property>
	 * 	</beans>
	 * TODO:优化 自定义注入：由于上面一种赋值方式，存在 1.配置文件代码冗余；2.系统里必然多个地方会引用同一个类型的bean->多次创建浪费 JVM（内存）资源
	 * 1.先声明被引用的 beans
	 * <beans id="userDAO" class="xxx"></>
	 * 2.再声明引用的 beans
	 * <beans id="userService" class="xxxImpl">
	 * <properties name="userDAO">
	 * 	<ref beans="userDAO"></ref>
	 * </properties>
	 * </beans>
	 */
	public void testInjectionOwner() {
		UserService bean = ctx.getBean(UserService.class);
		bean.register(new User("xjr", "xxx"));
		bean.login("xjr", "xxx");
	}

	public static void main(String[] args) {
		TestApp4Injection testApp = new TestApp4Injection();
		testApp.testInject();
		log.info("= = = = = =");
		testApp.testInjectComplexBean();
		log.info("= = = = = =");
		testApp.testInjectionOwner();
	}


}
